﻿// Project: Salient
// http://salient.codeplex.com
// 
// Copyright 2010, Sky Sanders <sky at skysanders.net>
// Dual licensed under the MIT or GPL Version 2 licenses.
// http://salient.codeplex.com/license
// 
// Date: April 25 2010 

#region

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Linq;
using System.Security.Permissions;
using System.Web;
using System.Web.Security;
using Salient.Web.DataAccess;
using Salient.Web.SQLite;
using Salient.Web.SQLite.Model;

#endregion

namespace Salient.Web.Security
{
    ///<summary>
    ///</summary>
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    public class SQLiteRoleProvider : RoleProvider
    {
        ///<summary>
        ///</summary>
        public string EntityConnectionString
        {
            get { return _entityConnectionString; }
        }
        #region Fields

        private string _applicationName;

        private string _entityConnectionString;

        

        #endregion

        #region Properties

        /// <summary>
        /// The name of the application using the custom membership provider.
        /// </summary>
        /// <returns>
        /// The name of the application using the custom membership provider.
        /// </returns>
        /// <remarks>
        /// The ApplicationName is used by the Roles class to associate users and roles with different applications. This enables multiple applications to use the same database to store user and role information without running into conflicts between duplicate user names or role names. Multiple ASP.NET applications can use the same database by specifying the same value in the ApplicationName property. The ApplicationName property can be set programmatically, or it can be set declaratively in the configuration file for the Web application using the applicationName attribute.
        ///
        /// If no ApplicationName property is specified, we recommend that the ApplicationVirtualPath property value be used.
        /// </remarks>
        public override string ApplicationName
        {
            get
            {
                if (String.IsNullOrEmpty(_applicationName))
                {
                    _applicationName = ProviderUtility.GetDefaultAppName();
                }
                return _applicationName;
            }
            set
            {
                if (value != null && value.Length > 256)
                {
                    throw new ProviderException(SR.GetString(SR.PersonalizationProviderApplicationNameExceedMaxLength, "256"));
                }
                _applicationName = value;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds the specified user names to the specified roles for the configured applicationName.
        /// </summary>
        /// <param name="usernames">A string array of user names to be added to the specified roles. </param>
        /// <param name="roleNames">A string array of the role names to add the specified user names to.</param>
        /// <remarks>
        /// <para>
        /// AddUsersToRoles is called by the Roles class to associate the specified users with the specified roles at the data 
        /// source. Roles are added to the configured ApplicationName.
        /// </para>
        /// <para>
        /// If any of the specified role names are not found for the configured applicationName, we recommend that your provider 
        /// throw a ProviderException.
        /// </para>
        /// <para>
        /// If any of the specified user names are not associated with any of the specified role names for the configured 
        /// applicationName, we recommend that your provider throw a ProviderException.
        /// </para>
        /// <para>
        /// If any of the specified user names is null or is an empty string, we recommend that your provider throw an exception.
        /// </para>
        /// <para>
        /// If any of the specified role names is null or is an empty string, we recommend that your provider throw an exception.
        /// </para>
        /// <para>
        /// If your data source supports transactions, we recommend that you include each add operation in a transaction and that 
        /// you roll back the transaction and throw an exception if any add operation fails.
        /// </para>
        /// </remarks>
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            ProviderUtility.CheckArrayParameter(ref roleNames, true, true, true, 256, "roleNames");
            ProviderUtility.CheckArrayParameter(ref usernames, true, true, true, 256, "usernames");

            List<string> usernamesLowered = new List<string>(usernames).Select(s => s.ToLower()).ToList();
            List<string> rolenamesLowered = new List<string>(roleNames).Select(s => s.ToLower()).ToList();

            //if role is missing throw RETURN(2)
            //if a user is already in a role throw  RETURN(3)

            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                

                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    Application app = GetOrCreateApplication(ctx);
                    using (var tx = holder.Connection.BeginTransaction())
                    {
                        IEnumerable<string> missingRoles =
                            rolenamesLowered.Except(
                                ctx.RolesView.Where(r => r.ApplicationId == app.ApplicationId).Select(
                                    r => r.LoweredRoleName));

                        if (missingRoles.Count() > 0)
                        {
                            throw new ProviderException(string.Format("Role(s) {0} do not exist.",
                                                                      string.Join(",", missingRoles.ToArray())));
                        }

                        IQueryable<UserInRoleViewMerged> alreadyInRoles =
                            ctx.UsersInRolesViewMerged.
                                Where(r => r.ApplicationId == app.ApplicationId).
                                Where(
                                ProviderUtility.BuildContainsExpression<UserInRoleViewMerged, string>(
                                    e => e.LoweredUserName, usernamesLowered)).
                                Where(
                                ProviderUtility.BuildContainsExpression<UserInRoleViewMerged, string>(
                                    e => e.LoweredRoleName, rolenamesLowered));

                        if (alreadyInRoles.Count() > 0)
                        {
                            throw new ProviderException("User already in role");
                        }

                        try
                        {
                            usernames.ToList().ForEach(username =>
                                {
                                    AspUser user = ctx.AspUsers.FirstOrDefault(u => u.LoweredUserName == username);
                                    if (user == null)
                                    {
                                        user = new AspUser
                                            {
                                                Application = app,
                                                LoweredUserName = username.ToLower(),
                                                UserName = username,
                                                LastActivityDate = ProviderUtility.Now()
                                            };
                                        ctx.AddToAspUsers(user);
                                    }
                                    roleNames.ToList().ForEach(rolename =>
                                        {
                                            Role role = ctx.Roles.First(r => r.LoweredRoleName == rolename.ToLower());
                                            user.Roles.Add(role);
                                        });
                                });
                            ctx.SaveChanges();
                            tx.Commit();
                        }
                        catch
                        {
                            tx.Rollback();
                            throw;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Adds a new role to the data source for the configured applicationName.
        /// </summary>
        /// <param name="roleName">The name of the role to create.</param>
        /// <remarks>
        /// CreateRole is called by the CreateRole method of the Roles class to add the specified role to the data source for 
        /// the configured ApplicationName.
        /// 
        /// If the specified role name already exists for the configured applicationName, is null, or is an empty string, we 
        /// recommend that your provider throw an exception.
        /// 
        /// If the specified role name contains a comma, we recommend that your provider throw an exception.
        /// 
        /// If your data source restricts the length of the role name, for example, through a fixed-length column of a table in 
        /// a database, we recommend that you throw an exception if the role name exceeds the maximum length allowed by the data 
        /// source.
        /// </remarks>
        public override void CreateRole(string roleName)
        {
            ProviderUtility.CheckParameter(ref roleName, true, true, true, 256, "roleName");

            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                

                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    Application app = GetOrCreateApplication(ctx);
                    using (var tx = holder.Connection.BeginTransaction())
                    {
                        if (ctx.Roles.Any(r => r.LoweredRoleName == roleName.ToLower()))
                        {
                            throw new ProviderException(SR.GetString(SR.ProviderRoleAlreadyExists, roleName));
                        }

                        try
                        {
                            Role role = new Role
                                {
                                    Application = app,
                                    LoweredRoleName = roleName.ToLower(),
                                    RoleName = roleName
                                };
                            ctx.AddToRoles(role);
                            ctx.SaveChanges();
                            tx.Commit();
                        }
                        catch (Exception ex)
                        {
                            tx.Rollback();
                            throw new ProviderException(SR.GetString(SR.ProviderUNKNOWNFailure), ex);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Removes a role from the data source for the configured applicationName.
        /// </summary>
        /// <returns>
        /// true if the role was successfully deleted; otherwise, false.
        /// </returns>
        /// <param name="roleName">The name of the role to delete.</param>
        /// <param name="throwOnPopulatedRole">If true, throw an exception if <paramref name="roleName"/> has one or more members 
        /// and do not delete <paramref name="roleName"/>.</param>
        /// <remarks>
        /// DeleteRole is called by the DeleteRole and the DeleteRole methods of the Roles class to delete the specified role from 
        /// the data source for the configured ApplicationName.
        /// 
        /// When you delete a role from the data source, ensure that you also delete any associations between a user name and the 
        /// deleted role for the configured applicationName.
        /// 
        /// If throwOnPopulatedRole is true, and the role identified by the roleName parameter has one or more members, throw a 
        /// ProviderException and do not delete the role. If throwOnPopulatedRole is false, then delete the role whether it is 
        /// empty or not.
        /// 
        /// If the specified role name does not exist, is null, or is an empty string, we recommend that your provider throw an exception.
        /// </remarks>
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            ProviderUtility.CheckParameter(ref roleName, true, true, true, 256, "roleName");

            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                

                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    using (var tx = holder.Connection.BeginTransaction())
                    {
                        try
                        {
                            Application app = GetOrCreateApplication(ctx);

                            RoleView roleV =
                                ctx.RolesView.FirstOrDefault(
                                    r => r.ApplicationId == app.ApplicationId && r.LoweredRoleName == roleName.ToLower());
                            if (roleV == null)
                            {
                                throw new ProviderException("Role does not exist");
                            }

                            if (ctx.UsersInRolesViewMerged.Any(u => u.RoleId == roleV.RoleId))
                            {
                                if (throwOnPopulatedRole)
                                {
                                    throw new ProviderException("Role is not empty");
                                }
                            }

                            Role role = ctx.Roles.First(r => r.RoleId == roleV.RoleId);
                            role.AspUsers.Load();
                            role.AspUsers.Clear();
                            ctx.DeleteObject(role);
                            ctx.SaveChanges();
                            tx.Commit();
                            return true;
                        }
                        catch
                        {
                            tx.Rollback();
                            throw;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets an array of user names in a role where the user name contains the specified user name to match.
        /// </summary>
        /// <returns>
        /// A string array containing the names of all the users where the user name matches <paramref name="usernameToMatch"/> 
        /// and the user is a member of the specified role.
        /// </returns>
        /// <param name="roleName">The role to search in.</param>
        /// <param name="usernameToMatch">The user name to search for.</param>
        /// <remarks>
        /// The FindUsersInRole method is called by the Roles class and returns a list of users in a role where the user name 
        /// contains a match of the supplied usernameToMatch for the configured applicationName. Wildcard support is included based 
        /// on the data source. Users are returned in alphabetical order by user name.
        /// 
        /// We recommend that you throw a ProviderException if roleName does not exist in the data source.
        /// </remarks>
        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            ProviderUtility.CheckParameter(ref roleName, true, true, true, 256, "roleName");
            ProviderUtility.CheckParameter(ref usernameToMatch, true, true, false, 256, "usernameToMatch");

            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                
                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    Application app = GetOrCreateApplication(ctx);
                    RoleView roleV = ctx.RolesView.FirstOrDefault(r => r.ApplicationId == app.ApplicationId);
                    if (roleV == null)
                    {
                        throw new ProviderException(SR.GetString(SR.ProviderRoleNotFound, roleName));
                    }
                    string loweredUsername = usernameToMatch.ToLower();
                    string[] roleUsers = ctx.UsersInRolesViewMerged.Where(r =>
                                                                          r.LoweredUserName.Contains(loweredUsername)).
                        OrderBy(r => r.UserName).
                        Select(r => r.UserName).ToArray();
                    return roleUsers;
                }
            }
        }

        /// <summary>
        /// Gets a list of all the roles for the configured applicationName.
        /// </summary>
        /// <returns>
        /// A string array containing the names of all the roles stored in the data source for the configured applicationName.
        /// </returns>
        /// <remarks>
        /// GetAllRoles is called by the GetAllRoles method of the Roles class to retrieve a list of role names from the data 
        /// source. Only the roles for the specified ApplicationName are retrieved.
        /// 
        /// If no roles exist for the configured applicationName, we recommend that your provider return a string array with 
        /// no elements.
        /// </remarks>
        public override string[] GetAllRoles()
        {
            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                
                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    Application app = GetOrCreateApplication(ctx);
                    string[] roles =
                        ctx.RolesView.Where(r => r.ApplicationId == app.ApplicationId).OrderBy(
                            r => r.RoleName).Select(r => r.RoleName).ToArray();
                    return roles;
                }
            }
        }

        /// <summary>
        /// Gets a list of the roles that a specified user is in for the configured applicationName.
        /// </summary>
        /// <returns>
        /// A string array containing the names of all the roles that the specified user is in for the configured applicationName.
        /// </returns>
        /// <param name="username">The user to return a list of roles for.</param>
        /// <remarks>
        /// GetRolesForUser is called by the GetRolesForUser method of the Roles class to retrieve the role names that the 
        /// specified user is associated with from the data source. Only the roles for the configured ApplicationName are retrieved.
        /// 
        /// If no roles exist for the specified user for the configured applicationName, we recommend that your provider return a 
        /// string array with no elements.
        /// 
        /// If the specified user name is null or is an empty string, we recommend that your provider throw an exception.
        /// </remarks>
        public override string[] GetRolesForUser(string username)
        {
            ProviderUtility.CheckParameter(ref username, true, false, true, 256, "username");
            if (username.Length < 1)
                return new string[0];
            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                
                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    Application app = GetOrCreateApplication(ctx);
                    string[] userRoles =
                        ctx.UsersInRolesViewMerged.Where(
                            r => r.ApplicationId == app.ApplicationId && r.LoweredUserName == username.ToLower()).
                            OrderBy(r => r.RoleName).Select(r => r.RoleName).ToArray();
                    return userRoles;
                }
            }
        }

        /// <summary>
        /// Gets a list of users in the specified role for the configured applicationName.
        /// </summary>
        /// <returns>
        /// A string array containing the names of all the users who are members of the specified role for the configured 
        /// applicationName.
        /// </returns>
        /// <param name="roleName">The name of the role to get the list of users for.</param>
        /// <remarks>
        /// GetUsersInRole is called by the GetUsersInRole method of the Roles class to retrieve the user names associated with a 
        /// role from the data source. Only the roles for the configured ApplicationName are retrieved.
        /// 
        /// If the specified role name does not exist for the configured applicationName or if it is null or an empty string, we 
        /// recommend that your provider throw an exception.
        /// 
        /// If no users are associated with the specified role for the configured applicationName, we recommend that your provider 
        /// return a string array with no elements.
        /// </remarks>
        public override string[] GetUsersInRole(string roleName)
        {
            ProviderUtility.CheckParameter(ref roleName, true, true, true, 256, "roleName");

            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                
                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    Application app = GetOrCreateApplication(ctx);
                    RoleView roleV =
                        ctx.RolesView.FirstOrDefault(
                            r => r.ApplicationId == app.ApplicationId && r.LoweredRoleName == roleName.ToLower());

                    if (roleV == null)
                    {
                        throw new ProviderException(SR.GetString(SR.ProviderRoleNotFound, roleName));
                    }

                    string[] roleUsers =
                        ctx.UsersInRolesViewMerged.Where(r => r.RoleId == roleV.RoleId).OrderBy(r => r.UserName).Select(
                            r => r.UserName).ToArray();

                    return roleUsers;
                }
            }
        }

        /// <summary>
        /// Initializes the provider.
        /// </summary>
        /// <param name="name">
        /// The friendly name of the provider.
        /// </param>
        /// <param name="config">
        /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.
        /// </param>
        /// <exception cref="T:System.ArgumentNullException">
        /// The name of the provider is null.
        /// </exception>
        /// <exception cref="T:System.ArgumentException">
        /// The name of the provider has a length of zero.
        /// </exception>
        /// <exception cref="T:System.InvalidOperationException">
        /// An attempt is made to call <see cref="M:System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection)" /> on a provider after the provider has already been initialized.
        /// </exception>
        public override void Initialize(string name, NameValueCollection config)
        {
            // Remove CAS from sample: HttpRuntime.CheckAspNetHostingPermission (AspNetHostingPermissionLevel.Low, SR.Feature_not_supported_at_this_level);
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (String.IsNullOrEmpty(name))
            {
                name = "SqlRoleProvider";
            }

            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", SR.GetString(SR.RoleSqlProviderDescription));
            }

            base.Initialize(name, config);

            

            _entityConnectionString = config["connectionStringName"];

            if (string.IsNullOrEmpty(_entityConnectionString))
            {
                throw new ProviderException(SR.GetString(SR.ConnectionNameNotSpecified));
            }

            _applicationName = config["applicationName"];

            if (string.IsNullOrEmpty(_applicationName))
            {
                _applicationName = ProviderUtility.GetDefaultAppName();
            }

            if (_applicationName.Length > 256)
            {
                throw new ProviderException(SR.GetString(SR.ProviderApplicationNameTooLong));
            }

            config.Remove("connectionStringName");
            config.Remove("applicationName");
            if (config.Count > 0)
            {
                string attribUnrecognized = config.GetKey(0);
                if (!String.IsNullOrEmpty(attribUnrecognized))
                {
                    throw new ProviderException(SR.GetString(SR.ProviderUnrecognizedAttribute, attribUnrecognized));
                }
            }

            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    GetOrCreateApplication(ctx);
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether the specified user is in the specified role for the configured applicationName.
        /// </summary>
        /// <returns>
        /// true if the specified user is in the specified role for the configured applicationName; otherwise, false.
        /// </returns>
        /// <param name="username">The user name to search for.</param>
        /// <param name="roleName">The role to search in.</param>
        /// <remarks>
        /// The IsUserInRole method is called by the IsUserInRole method of the Roles class to determine whether the current 
        /// logged-on user is associated with a role from the data source for the configured ApplicationName.
        /// 
        /// If the specified user name is null or is an empty string, we recommend that your provider throw an exception.
        /// 
        /// If the specified role name is null or is an empty string, we recommend that your provider throw an exception.
        /// </remarks>
        public override bool IsUserInRole(string username, string roleName)
        {
            ProviderUtility.CheckParameter(ref roleName, true, true, true, 256, "roleName");
            ProviderUtility.CheckParameter(ref username, true, false, true, 256, "username");
            if (username.Length < 1)
                return false;

            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                
                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    Application app = GetOrCreateApplication(ctx);
                    return ctx.UsersInRolesViewMerged.Any(
                        r =>
                        r.ApplicationId == app.ApplicationId &&
                        r.LoweredUserName == username.ToLower() &&
                        r.LoweredRoleName == roleName.ToLower());
                }
            }
        }

        /// <summary>
        /// Removes the specified user names from the specified roles for the configured applicationName.
        /// </summary>
        /// <param name="usernames">A string array of user names to be removed from the specified roles. </param>
        /// <param name="roleNames">A string array of role names to remove the specified user names from.</param>
        /// <remarks>
        /// RemoveUsersFromRoles is called by RemoveUserFromRole , RemoveUsersFromRole , RemoveUserFromRoles , and 
        /// RemoveUsersFromRoles methods of the Roles class to remove the specified users from the specified roles at the data 
        /// source. Only roles for the configured ApplicationName are modified.
        /// 
        /// If any of the specified role names are not found for the configured applicationName, we recommend that your provider 
        /// throw a ProviderException.
        /// 
        /// If any of the specified user names are not associated with any of the specified role names for the configured 
        /// applicationName, we recommend that your provider throw a ProviderException.
        /// 
        /// If any of the specified user names is null or is an empty string, we recommend that your provider throw an exception.
        /// 
        /// If any of the specified role names is null or is an empty string, we recommend that your provider throw an exception.
        /// 
        /// If your data source supports transactions, we recommend that you include each remove operation in a transaction and 
        /// that you  roll back the transaction and throw an exception if any remove operation fails.
        /// </remarks>
        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            ProviderUtility.CheckArrayParameter(ref roleNames, true, true, true, 256, "roleNames");
            ProviderUtility.CheckArrayParameter(ref usernames, true, true, true, 256, "usernames");

            List<string> rolenamesLowered = new List<string>(roleNames).Select(s => s.ToLower()).ToList();

            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                
                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    Application app = GetOrCreateApplication(ctx);
                    using (var tx = holder.Connection.BeginTransaction())
                    {
                        try
                        {
                            IEnumerable<string> missingRoles =
                                rolenamesLowered.Except(
                                    ctx.RolesView.Where(r => r.ApplicationId == app.ApplicationId).
                                        Select(r => r.LoweredRoleName));

                            if (missingRoles.Count() > 0)
                            {
                                throw new ProviderException(SR.GetString(SR.ProviderRoleNotFound,
                                                                         string.Join(",", missingRoles.ToArray())));
                            }

                            // I banged my head against the wall for a few hours trying to come up with
                            // a more elegant way to accomplish this but, ultimately, iteration is the
                            // easiest to read and write. No dire need for performance here anyway.

                            usernames.ToList().ForEach(userName =>
                                {
                                    AspUserMergedView userV =
                                        ctx.AspUsersMergedView.FirstOrDefault(
                                            u =>
                                            u.ApplicationId == app.ApplicationId &&
                                            u.LoweredUserName == userName.ToLower());
                                    if (userV == null)
                                    {
                                        throw new ProviderException(SR.GetString(SR.ProviderThisUserNotFound, userName));
                                    }
                                    AspUser user = ctx.AspUsers.First(u => u.UserId == userV.UserId);

                                    user.Roles.Load();

                                    user.Roles.ToList().ForEach(r =>
                                        {
                                            string loweredRoleName = r.LoweredRoleName;
                                            if (!rolenamesLowered.Contains(loweredRoleName))
                                            {
                                                throw new ProviderException(
                                                    SR.GetString(SR.ProviderThisUserAlreadyNotInRole, userName,
                                                                 r.RoleName));
                                            }
                                            user.Roles.Remove(r);
                                        });
                                });

                            ctx.SaveChanges();
                            tx.Commit();
                        }
                        catch
                        {
                            tx.Rollback();
                            throw;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether the specified role name already exists in the role data source for the configured 
        /// applicationName.
        /// </summary>
        /// <returns>
        /// true if the role name already exists in the data source for the configured applicationName; otherwise, false.
        /// </returns>
        /// <param name="roleName">The name of the role to search for in the data source.</param>
        /// <remarks>
        /// RoleExists is called by the RoleExists method of the Roles class to determine whether a role name exists in the data 
        /// source for the configured ApplicationName.
        /// 
        /// If the specified role name is null or is an empty string, we recommend that your provider throw an exception.
        /// </remarks>
        public override bool RoleExists(string roleName)
        {
            ProviderUtility.CheckParameter(ref roleName, true, true, true, 256, "roleName");

            using (
                var holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                

                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    Application app = GetOrCreateApplication(ctx);
                    return
                        ctx.RolesView.Any(
                            r => r.LoweredRoleName == roleName.ToLower() && r.ApplicationId == app.ApplicationId);
                }
            }
        }

        #endregion

        #region Private Methods


        private Application GetOrCreateApplication(AspNetDbEntities entities)
        {
            return ProviderUtility.GetOrCreateApplication(entities, ApplicationName);
        }

        #endregion

    }
}